package top.superflow.flow.runtime.mapper

import java.util

import top.superflow.exception.Exceptable
import top.superflow.flow.runtime.flow.{FlowRequest, FlowResponse}
import top.superflow.flow.runtime.inf.{GeneralRequest, GeneralResponse, IOSchema, ParamTypeSchema}
import top.superflow.flow.runtime.process.ProcessRequest
import top.superflow.log.Loggable

import scala.collection.mutable
import scala.collection.mutable.ListBuffer

object AutoMatchUtils extends Loggable with Exceptable{

  def extractProcessRequestByAutoMatch(flowRequest: FlowRequest, flowResponse: FlowResponse, targetSchema : IOSchema): ProcessRequest = {
    val processRequest = new ProcessRequest
    val params = mutable.Map[String, Any]()

    val realTargetSchema = targetSchema.getSchema

    for((expectedParamName, expectedParamType) <- realTargetSchema){
      var paramValue = searchResponse(flowResponse, expectedParamName, expectedParamType)
      if(paramValue == null){
        paramValue = searchRequest(flowRequest, expectedParamName, expectedParamType)
      }

      if(paramValue == null){
        logger.info("The parameter {} has not been found", expectedParamName)
      }
      params.put(expectedParamName, paramValue)
    }

    processRequest.addParameters(params.toMap)

    processRequest
  }

  private def searchResponse(generalResponse : Option[GeneralResponse], expectedParamName : String, expectedParamType : ParamTypeSchema) : Any = {
    if(generalResponse == None){
      return null
    }

    if(expectedParamType == null){
      throwRequestUnsupportedException("The type of parameter should not be null!")
    }

    val responseObject = generalResponse.get.getResponse
    if(responseObject == null){
      return null
    }

    if(classOf[Map[String,_]].isAssignableFrom(responseObject.getClass)){
      val expectedValue = responseObject.asInstanceOf[Map[String,_]].get(expectedParamName)
      if(expectedValue != None){
        return expectedValue.get
      }
    }

    if(classOf[mutable.Map[String, _]].isAssignableFrom(responseObject.getClass)){
      val expectedValue = responseObject.asInstanceOf[mutable.Map[String, _]].get(expectedParamName)
      if(expectedValue != None){
        return expectedValue.get
      }
    }

    if(classOf[util.Map[String, _]].isAssignableFrom(responseObject.getClass)){
      val expectedValue = responseObject.asInstanceOf[util.Map[String, _]].get(expectedParamName)
      if(expectedValue != null){
        return expectedValue
      }
    }

    val expectedField = try {
      responseObject.getClass.getField(expectedParamName)
    } catch {
      case e : NoSuchFieldException =>{
        return null
      }
    }
    expectedField.get(responseObject)
  }

  private def searchResponse(flowResponse : FlowResponse, expectedParamName : String, expectedParamType : ParamTypeSchema) : Any = {
    val allRoutePath = ListBuffer[String]()
    allRoutePath ++= flowResponse.getRoutePath
    val reversedRoutePath = allRoutePath.reverse

    for(route <- reversedRoutePath){
      val responseOption = flowResponse.getAllResponses.get(route)
      val paramValue = searchResponse(responseOption, expectedParamName, expectedParamType)
      if(paramValue != null){
        return paramValue
      }
    }

    return null
  }


  private def searchRequest(generalRequest: Option[GeneralRequest], expectedParamName : String, expectedParamType : ParamTypeSchema) : Any = {
    if(generalRequest == None){
      return null
    }

    if(expectedParamType == null){
      throwRequestUnsupportedException("The type of parameter should not be null!")
    }

    val allParameters = generalRequest.get.getParameters
    for((paramName, paramValue) <- allParameters){
      if(expectedParamType.isMatch(paramValue)){
        return paramValue
      }
    }

    return null
  }

  private def searchRequest(flowRequest: FlowRequest, expectedParamName : String, expectedParamType : ParamTypeSchema) : Any = {
    val allRoutePath = ListBuffer[String]()
    allRoutePath ++= flowRequest.getRoutePath
    val reversedRoutePath = allRoutePath.reverse

    for(route <- reversedRoutePath){
      val requestOption = flowRequest.getAllReports.get(route)
      val paramValue = searchRequest(requestOption, expectedParamName, expectedParamType)
      if(paramValue != null){
        return paramValue
      }
    }

    return null

  }
}
